/*
 * GameController.cpp
 *
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses 
 */

#include "GameController.h"
#include <time.h>
#include <iostream>
#include "ModelConstants.h"
#include "Plant.h"

using std::map;
using std::list;
using std::pair;

GameController::GameController(Map& map)
    : map(map) {
    running = false;
    id_count = 0;
    remaining_enemies = 0;
}

void GameController::setRemainingEnemies(int enemies) {
    remaining_enemies = enemies;
}

int GameController::getRemainingEnemies() {
    return remaining_enemies;
}

void GameController::addObserver(GameEventObserver* observer) {
    observers.push_back(observer);
}

int GameController::addBomberman(Bomberman* bomberman) {
    bombermans[setUpObject(bomberman)] = bomberman;
    return id_count;
}

Bomberman* GameController::getBomberman(int id) {
    return bombermans[id];
}

int GameController::addLivingObject(LivingObject* object, int level) {
    living_objects[setUpObject(object, level)] = object;
    return id_count;
}

LivingObject* GameController::getLivingObject(int id) {
    return living_objects[id];
}

int GameController::addSolid(MapObject* solid, int level) {
    solids[setUpObject(solid, level)] = solid;
    return id_count;
}

MapObject* GameController::getSolid(int id) {
    return solids[id];
}

void GameController::addSpawner(Spawner* spawner, bool isAnObject) {
    spawners.push_back(pair<Spawner*, bool>(spawner, isAnObject));
}

bool GameController::isRunning() {
    return running;
}

GameState GameController::getState() {
    if (running) {
        return IN_PROGRESS;
    } else {
        if (remaining_enemies == 0) {
            return WON;
        } else if (bombermans.size() == 0) {
            return LOST;
        }
        return NOT_STARTED;
    }
}

void GameController::startGame() {
    running = true;
    while (running) {
        std::map<int, Bomberman*>::iterator bombermans_it;
        for (bombermans_it = bombermans.begin(); bombermans_it != bombermans.end(); ++bombermans_it) {
            Bomberman* bomberman = bombermans_it->second;
            if (! bomberman->canRevive()) {
                continue;
            }
            if ((! bomberman->hasEnteredTheMap()) && (bomberman->isDestroyed())) {
                reinsertBomberman(bomberman);
            } else {
                bomberman->move(map);
            }
        }
        std::map<int, LivingObject*>::iterator objects_it;
        for (objects_it = living_objects.begin(); objects_it != living_objects.end(); ++objects_it) {
            LivingObject* object = objects_it->second;
            if (! object->isDestroyed()) {
                object->live(map);
            }
        }
        list<pair<Spawner*, bool> >::iterator spawners_it;
        for (spawners_it = spawners.begin(); spawners_it != spawners.end(); ++spawners_it) {
            Spawner* spawner = (*spawners_it).first;
            if (spawner->isDepleted()) {
                continue;
            } else if (spawner->canSpawn(map)) {
                addLivingObject(spawner->spawn());
            }
        }
        cleanUp();
        milisleep(250);
        if (remaining_enemies == 0) {
            //Victory
            running = false;
        } else if (bombermans.size() == 0) {
            //Defeat
            running = false;
        }
    }
}

void GameController::stopGame() {
    running = false;
}

GameController::~GameController() {
    std::map<int, Bomberman*>::iterator bombermans_it;
    for (bombermans_it = bombermans.begin(); bombermans_it != bombermans.end(); ++bombermans_it) {
        delete bombermans_it->second;
    }
    bombermans.clear();
    std::map<int, LivingObject*>::iterator objects_it;
    for (objects_it = living_objects.begin(); objects_it != living_objects.end(); ++objects_it) {
        delete objects_it->second;
    }
    living_objects.clear();
    std::map<int, MapObject*>::iterator solids_it;
    for (solids_it = solids.begin(); solids_it != solids.end(); ++solids_it) {
        delete solids_it->second;
    }
    solids.clear();
    list<pair<Spawner*, bool> >::iterator spawners_it;
    for (spawners_it = spawners.begin(); spawners_it != spawners.end(); ++spawners_it) {
        if (! (*spawners_it).second) {
            delete (*spawners_it).first;
        }
    }
    spawners.clear();
    std::list<GameEventObserver*>::iterator observers_it;
    for (observers_it = observers.begin(); observers_it != observers.end(); ++observers_it) {
        delete (*observers_it);
    }
    observers.clear();
}

int GameController::setUpObject(MapObject* object, int level) {
    id_count++;
    object->setId(id_count);
    std::list<GameEventObserver*>::iterator it;
    for (it = observers.begin(); it != observers.end(); ++it) {
        object->attach(*it);
    }
    if (level == OBJECT) {
        map.insertObject(object, object->getX(), object->getY());
    } else if (level == DROP) {
        map.dropObject(object, object->getX(), object->getY());
    }
    return id_count;
}

void GameController::milisleep(int miliseconds) {
    struct timespec time, ret;
    time.tv_sec = 0;
    time.tv_nsec = miliseconds * 1000000L;
    nanosleep(&time, &ret);
}

void GameController::cleanUp() {
    std::map<int, Bomberman*>::iterator bombermans_it;
    for (bombermans_it = bombermans.begin(); bombermans_it != bombermans.end();) {
        if (! bombermans_it->second->canRevive()) {
            delete bombermans_it->second;
            bombermans.erase(bombermans_it++);
        } else {
            ++bombermans_it;
        }
    }
    std::map<int, LivingObject*>::iterator objects_it;
    for (objects_it = living_objects.begin(); objects_it != living_objects.end();) {
        if (objects_it->second->isDestroyed()){
            if (objects_it->second->getAlignment() == AL_ENEMY) {
                remaining_enemies--;
            }
            delete objects_it->second;
            living_objects.erase(objects_it++);
        } else {
            ++objects_it;
        }
    }
    std::map<int, MapObject*>::iterator solids_it;
    for (solids_it = solids.begin(); solids_it != solids.end();) {
        if (solids_it->second->isDestroyed()){
            delete solids_it->second;
            solids.erase(solids_it++);
        } else {
            ++solids_it;
        }
    }
    list<pair<Spawner*, bool> >::iterator spawners_it;
    for (spawners_it = spawners.begin(); spawners_it != spawners.end();) {
        Spawner* spawner = (*spawners_it).first;
        bool isAnObject = (*spawners_it).second;
        if ((spawner->isDepleted()) && (! isAnObject)) {
            delete spawner;
            spawners.erase(spawners_it++);
        } else {
            ++spawners_it;
        }
    }
}

void GameController::reinsertBomberman(Bomberman* bomberman) {
    //It tries 10 times, and continues if it can't find a free spawn
    for (int t = 0; t < 10; t++) {
        pair<int, int> spawn_point = map.getRandomBombermanSpawn();
        int spawn_x = spawn_point.first;
        int spawn_y = spawn_point.second;
        if (! map.tileIsOccupied(spawn_x, spawn_y)) {
            bomberman->restoreResistance();
            bomberman->setX(spawn_x);
            bomberman->setY(spawn_y);
            map.insertObject(bomberman, spawn_x, spawn_y);
            break;
        }
    }
}
